package com.google.common.collect;

import com.google.common.base.Preconditions;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

@SuppressWarnings("unchecked")
public class Lists {
    public static <E> ArrayList<E> newArrayList() {
        return new ArrayList<>();
    }
    public static <E> ArrayList<E> newArrayList(int capacity) {
        return new ArrayList<>(capacity);
    }

    @SafeVarargs
    public static <E> ArrayList<E> newArrayList(E... elements) {
        if (null == elements || elements.length == 0) {
            return newArrayList(0);
        }
        ArrayList<E> list = new ArrayList<>(elements.length);
        Collections.addAll(list, elements);
        return list;
    }
    public static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
        Objects.requireNonNull(elements);
        return elements instanceof Collection ? new ArrayList<>((Collection<E>)elements) : newArrayList(elements.iterator());
    }
    public static <E> ArrayList<E> newArrayList(Iterator<? extends E> elements) {
        ArrayList<E> list = newArrayList();
        Iterators.addAll(list, elements);
        return list;
    }

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
        return new CopyOnWriteArrayList<>();
    }

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(Iterable<? extends E> elements) {
        Collection<? extends E> elementsCollection = elements instanceof Collection ? (Collection<? extends E>)elements : newArrayList(elements);
        return new CopyOnWriteArrayList<>(elementsCollection);
    }
    public static <T> List<List<T>> partition(List<T> list, int size) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return new Partition<T>(list, size);
    }

    private static class Partition<T> extends AbstractList<List<T>> {
        final List<T> list;
        final int size;

        Partition(List<T> list, int size) {
            this.list = list;
            this.size = size;
        }

        public List<T> get(int index) {
            Preconditions.checkElementIndex(index, this.size());
            int start = index * this.size;
            int end = Math.min(start + this.size, this.list.size());
            return this.list.subList(start, end);
        }

        public int size() {
            return (int) Math.ceil(1.0*this.list.size()/this.size);
        }

        public boolean isEmpty() {
            return this.list.isEmpty();
        }
    }
}
